/*
 * TablicaSymboli.cpp
 *
 *  Created on: Nov 24, 2011
 *      Author: bkoziak
 */
#include "TablicaSymboli.h"
using namespace std;

TablicaSymboli* TablicaSymboli::instance = NULL;

TablicaSymboli* TablicaSymboli::getInstance() {
	if (instance == NULL) {
		cout << "Tworze tablice symboli" << endl;
		instance = new TablicaSymboli();
		Symbol* wr = instance->insert("write", ID);
		wr->setTypSymbolu(Symbol::FUNKCJA_VM);
		Symbol* rd = instance->insert("read", ID);
		rd->setTypSymbolu(Symbol::FUNKCJA_VM);
	}
	return instance;
}

int TablicaSymboli::getOstatniAdresStosu() const {
	return ostatniAdresStosu;
}

void TablicaSymboli::setOstatniAdresStosu(int ostatniAdresStosu) {
	this->ostatniAdresStosu = ostatniAdresStosu;
}

TablicaSymboli::TablicaSymboli() {
	aktualnyZakres = 0;
	deklaracjeRozpoczete = false;
	ostatnieTymczasoweId = 0;
	ostatniAdresStosu = 0;
	ostatniAdres = 0;
	lokalnyZakres = 0;
	aktualnaProceduraLubFunkcja = NULL;

}

Symbol* TablicaSymboli::insert(string lexem, int token) {
	cout << "Probuje wstawic symbol : " << lexem << endl;
	Symbol* symbol = this->pobierzSymbolWAktualnymZakresie(lexem);
	if (symbol != NULL) {
		cout << "Symbol '" << lexem << "' znaleziony w zakresie" << endl;
		return symbol;
	}
	symbol = new Symbol(lexem, token);
	symbol->setZakres(aktualnyZakres);
	if (token == NUM) {
		symbol->setTypSymbolu(Symbol::STALA);
		symbol->setStalaWartosc(toDouble(lexem));
		if (symbol->getLexem().find(".") != string::npos
				|| symbol->getLexem().find("e") != string::npos) {
			symbol->setTypZmiennej(Symbol::REAL);
		} else {
			symbol->setTypZmiennej(Symbol::INTEGER);
		}

	}
	this->mapaSymboli.insert(MapaSymboli::value_type(lexem, symbol));
	cout << "Dodano symbol '" + lexem << "'" << endl;
	symbol->log(1);
	return symbol;
}
Symbol* TablicaSymboli::pobierzSymbolWAktualnymZakresie(string lexem) {
	cout << "Wyszukuje po kluczu : " << lexem << endl;
	pair<MapaSymboli::iterator, MapaSymboli::iterator> zakresWartosci =
			this->mapaSymboli.equal_range(lexem);
	while (zakresWartosci.first != zakresWartosci.second) {
		if (zakresWartosci.first->second->getZakres() == this->aktualnyZakres) {
			return zakresWartosci.first->second;
		}
		++zakresWartosci.first;
	}

	return NULL;
}

Symbol* TablicaSymboli::pobierzSymbolWZakresie(string lexem, int zakres) {
	cout << "Wyszukuje po kluczu : " << lexem << endl;
	pair<MapaSymboli::iterator, MapaSymboli::iterator> zakresWartosci =
			this->mapaSymboli.equal_range(lexem);
	while (zakresWartosci.first != zakresWartosci.second) {
		if (zakresWartosci.first->second->getZakres() == zakres) {
			return zakresWartosci.first->second;
		}
		++zakresWartosci.first;
	}

	return NULL;
}

Symbol* TablicaSymboli::stworzZmiennaTymczasowa(Symbol::TYP_ZMIENNEJ typ) {
	stringstream nazwa;
	nazwa << "$t" << ostatnieTymczasoweId++;
	Symbol* zmiennaTymczasowa = new Symbol(nazwa.str(), ID);
	zmiennaTymczasowa->setZakres(aktualnyZakres);
	zmiennaTymczasowa->setTypSymbolu(Symbol::ZMIENNA);
	zmiennaTymczasowa->setTypZmiennej(typ);
	nazwyZmiennychTymczasowych.push_back(nazwa.str());
	mapaSymboli.insert(MapaSymboli::value_type(nazwa.str(), zmiennaTymczasowa));
	return zmiennaTymczasowa;
}

void TablicaSymboli::wyczyscAktualneID() {
	cout << "czyszcze wektor symboli" << endl;
	aktualneID.clear();
}

void TablicaSymboli::dodajDoAktualnychID(Symbol* symbol) {
	cout << "dodaje do wektora symboli " << symbol->getLexem() << endl;
	aktualneID.push_back(symbol);
}

void TablicaSymboli::ustawTypDlaAktualnychID(Symbol::TYP_ZMIENNEJ typZmiennej) {
	cout << "Ustawiam typ dla wektora symboli : "
			<< Symbol::nazwaTypuZmiennej(typZmiennej) << endl;
	//Dla kazdego symbolu ustawiamy typ zgodny z typem podanym w identifier_list
	for (unsigned int i = 0; i < aktualneID.size(); ++i) {
		Symbol* symbol = aktualneID.at(i);
		if (symbol->getTypSymbolu() != Symbol::NIEZDEFINIOWANY) {
			//TODO: Blad
		}
		symbol->setTypSymbolu(Symbol::ZMIENNA);
		symbol->setTypZmiennej(typZmiennej);

		if (symbol->getAdresowanie() == Symbol::NIEZDEFINIOWANY) {
			symbol->setAdresowanie(Symbol::BEZPOSREDNIE);
		}
		//TODO: Obsluga dla tablic ???

	}
}

int TablicaSymboli::zaallokujPamiec(int rozmiar) {
	if (aktualnyZakres == 0) {
		int ostatniAdres = this->ostatniAdres;
		this->ostatniAdres += rozmiar;
		return ostatniAdres;
	} else{
		ostatniAdresStosu -= rozmiar;
		return ostatniAdres;
	}
}
void TablicaSymboli::zaallokujPamiecDlaAktualnychID() {
	for (unsigned int i = 0; i < aktualneID.size(); ++i) {
		Symbol* symbol = aktualneID.at(i);
		symbol->setAdres(this->zaallokujPamiec(symbol->getRozmiar()));
		cout << "Zaallokowano pamiec dla " << symbol->getLexem()
				<< " w zakresie " << symbol->getZakres() << " adres : "
				<< symbol->getAdres();
	}
}
void TablicaSymboli::zaallokujPamiecDlaZmiennychTymczasowych() {
	for (unsigned int i = 0; i < nazwyZmiennychTymczasowych.size(); ++i) {
		Symbol* zTymczasowa = pobierzSymbolWAktualnymZakresie(
				nazwyZmiennychTymczasowych.at(i));
		if (zTymczasowa != NULL) {
			if (zTymczasowa->getAdres() == Symbol::NIEZDEFINIOWANY) {
				zTymczasowa->setAdres(
						zaallokujPamiec(zTymczasowa->getRozmiar()));
			}
		}

	}
}

int TablicaSymboli::otworzZakres() {
	if (aktualnyZakres != 0) {
		cout << "nie mozna zagniezdzac zakresow" << endl;
		//TODO: zgloszenie bledu
	}

	lokalnyZakres += 1;
	aktualnyZakres = lokalnyZakres;
	wyczyscAktualneID();
	wyczyscAktualneParametry();
	wyczyscWektorWyrazen();
	return aktualnyZakres;
}

int TablicaSymboli::zamknijZakres() {
	if (aktualnyZakres == 0) {
		cout << "nie mozna zamknac globalnego zakresu" << endl;
		//TODO: zgloszenie bledu
	}
	aktualnaProceduraLubFunkcja->setRozmiarPamieciFunkcji(-ostatniAdresStosu);
	ostatniAdresStosu = 0;
	aktualnyZakres = 0;
	return aktualnyZakres;
}

void TablicaSymboli::deklarujFunkcje(Symbol* funkcja,
		Symbol::TYP_ZMIENNEJ typZwracany) {
	funkcja->setTypSymbolu(Symbol::FUNCKJA);
	funkcja->setTypZmiennej(typZwracany);
	deklarujWspolneDlaProceduryIFUnkcji(funkcja);

	Symbol* returnVal = insert(funkcja->getLexem(), ID);
	returnVal->setTypSymbolu(Symbol::ZMIENNA);
	returnVal->setTypZmiennej(typZwracany);
	returnVal->setAdres(8);
	returnVal->setAdresowanie(Symbol::POSREDNIE);
}

void TablicaSymboli::deklarujProcedure(Symbol* procedura) {
	procedura->setTypSymbolu(Symbol::PROCEDURA);
	deklarujWspolneDlaProceduryIFUnkcji(procedura);
}
void TablicaSymboli::deklarujWspolneDlaProceduryIFUnkcji(Symbol* symbol) {
	symbol->setZakresFunkcyjny(this->getAktualnyZakres());
	setAktualnaProceduraLubFunkcja(symbol);
	przypiszParametry(symbol);
	zaallokujParametry();

	symbol->log(0);

}

void TablicaSymboli::przypiszParametry(Symbol* symbol) {
	mapaParametrowFunkcji.insert(
			MapaParametrowFunkcji::value_type(symbol, aktualneID));
	MapaParametrowFunkcji::iterator iterator = mapaParametrowFunkcji.find(
			symbol);
	WektorSymboli parametry = iterator->second;
	cout << "Funkcja " << symbol->getLexem() << " - parametry:" << endl;
	for (unsigned int i = 0; i < parametry.size(); ++i) {
		Symbol* parametr = parametry.at(i);
		parametr->setAdresowanie(Symbol::POSREDNIE);
		cout << i << ". " << parametr->getLexem();
		parametr->log(1);
	}
}

void TablicaSymboli::zaallokujParametry() {
	//TODO:OGARNAC!!!
	int startOffset;
	if (aktualnaProceduraLubFunkcja->getTypSymbolu() == Symbol::PROCEDURA) {
		startOffset = 8;
	} else {
		startOffset = 12;
	}
	for (int i = aktualneID.size() - 1; i >= 0; --i) {
		Symbol* parametr = aktualneID.at(i);
		parametr->setAdres(startOffset);
		startOffset += 4;

	}
	//rozmiar naglowka funkcji pomniejszony o retaddr i oldBP
	aktualnaProceduraLubFunkcja->setRozmiarNaglowkaFunkcji(startOffset - 8);
}

void TablicaSymboli::wyczyscAktualneParametry() {
	aktualneParametry.clear();
}

void TablicaSymboli::dodajAktualneIdDoParametrow() {
	for (unsigned int i = 0; i < aktualneID.size(); ++i) {
		aktualneParametry.push_back(aktualneID.at(i));
	}
}

Symbol* TablicaSymboli::sprawdzPoprawnoscWywolania(Symbol* wywolywanaFunkcja) {
	Symbol* funkcja = this->pobierzSymbolWZakresie(
			wywolywanaFunkcja->getLexem(), 0);
	if (funkcja == NULL) {
		//TODO: obsluga bledu
	} else if (funkcja->getTypSymbolu() == Symbol::FUNKCJA_VM) {
		if (aktualneWyrazenia.size() < 1) {
			//TODO: obsluga bledu
		}
		return funkcja;
	} else if (funkcja->getTypSymbolu() != Symbol::FUNCKJA
			|| funkcja->getTypSymbolu() != Symbol::PROCEDURA) {
		//TODO: obsluga bledu
	}

	MapaParametrowFunkcji::iterator it = this->mapaParametrowFunkcji.find(
			funkcja);
	WektorSymboli parametry = it->second;
	if (parametry.size() != this->aktualneParametryFunkcji.size()) {
		//TODO: obsluga bledu blednej ilosci parametrow
	}

	return funkcja;
}

WektorSymboli TablicaSymboli::pobierzWektorParametrowFunkcji(Symbol* funkcja) {
	MapaParametrowFunkcji::iterator it = mapaParametrowFunkcji.find(funkcja);
	if (it == mapaParametrowFunkcji.end()) {
		return WektorSymboli();
	}
	return it->second;
}

void TablicaSymboli::wstawDoWektoraWyrazen(Symbol* symbol) {
	aktualneWyrazenia.push_back(symbol);
}

void TablicaSymboli::wyczyscWektorWyrazen() {
	aktualneWyrazenia.clear();
}
WektorSymboli TablicaSymboli::pobierzWektorWyrazen() {
	return aktualneWyrazenia;
}

/*
 * UTILS
 */

double TablicaSymboli::toDouble(string lexem) {
	stringstream konwerter;
	konwerter << lexem;
	double wartosc;
	konwerter >> wartosc;
	return wartosc;
}

/*
 * UTILS
 */
void TablicaSymboli::log() {
	if (LOG_TABLICA) {
		for (MapaSymboli::iterator it = mapaSymboli.begin();
				it != mapaSymboli.end(); ++it) {
			it->second->log(0);
		}
	}
}

/*
 * SETTERY I GETTERY
 */

void TablicaSymboli::rozpocznijDeklaracje() {
	this->deklaracjeRozpoczete = true;
}
void TablicaSymboli::zakonczDeklaracje() {
	this->deklaracjeRozpoczete = false;
}
bool TablicaSymboli::wTrakcieDeklaracji() {
	return this->deklaracjeRozpoczete;
}

int TablicaSymboli::getAktualnyZakres() const {
	return aktualnyZakres;
}

void TablicaSymboli::setAktualnyZakres(int aktualnyZakres) {
	this->aktualnyZakres = aktualnyZakres;
}
int TablicaSymboli::getLokalnyZakres() const {
	return lokalnyZakres;
}

void TablicaSymboli::setLokalnyZakres(int lokalnyZakres) {
	this->lokalnyZakres = lokalnyZakres;
}
Symbol *TablicaSymboli::getAktualnaProceduraLubFunkcja() const {
	return aktualnaProceduraLubFunkcja;
}

void TablicaSymboli::setAktualnaProceduraLubFunkcja(
		Symbol *aktualnaProceduraLubFunkcja) {
	this->aktualnaProceduraLubFunkcja = aktualnaProceduraLubFunkcja;
}
TablicaSymboli::~TablicaSymboli() {
	// TODO Auto-generated destructor stub
}
